package com.rainbow.support.mybatis.config;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.rainbow.support.mybatis.handler.MyBatisMetaObjectHandler;
import com.rainbow.support.mybatis.injector.CustomSqlInjector;
import com.rainbow.support.mybatis.properties.MybatisExtraProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;

import java.util.Collections;
import java.util.List;

/**
 * Mybatis 常用重用拦截器
 * <p>
 * 拦截器执行一定是：
 * WriteInterceptor 优先于 DataScopeInterceptor 优先于 PaginationInterceptor
 * @author 付为地
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(MybatisExtraProperties.class)
@ConditionalOnProperty(prefix= MybatisExtraProperties.PREFIX,name = "enable", havingValue = "true",matchIfMissing = true)
public abstract class MybatisConfiguration {

    private final MybatisExtraProperties properties;

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,
     * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
     * 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    @Order(5)
    @Primary
    @ConditionalOnMissingBean
    /*@ConditionalOnExpression("${extend.mybatis.plus.pageable:true}")*/
    @ConditionalOnExpression(MybatisExtraProperties.PLUS_INTERCEPTOR_SPEL)
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        List<InnerInterceptor> beforeInnerInterceptor = getPaginationBeforeInnerInterceptor();
        if (!beforeInnerInterceptor.isEmpty()) {
            beforeInnerInterceptor.forEach(interceptor::addInnerInterceptor);
        }
        // 新增MYSQL分页拦截器,一定要先设置租户判断后才进行分页拦截设置
        // 分页插件: PaginationInnerInterceptor
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(properties.getDbType());
        paginationInnerInterceptor.setMaxLimit(properties.getMaxLimit());
        //防止全表更新与删除插件: BlockAttackInnerInterceptor
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        List<InnerInterceptor> afterInnerInterceptor = getPaginationAfterInnerInterceptor();
        if (!afterInnerInterceptor.isEmpty()) {
            afterInnerInterceptor.forEach(interceptor::addInnerInterceptor);
        }
        if (properties.isBlockAttack()) {
            BlockAttackInnerInterceptor blockAttackInnerInterceptor = new BlockAttackInnerInterceptor();
            interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
        }
        // sql性能规范插件，限制比较多，慎用哦
        if (properties.isIllegalSql()) {
            IllegalSQLInnerInterceptor isi = new IllegalSQLInnerInterceptor();
            interceptor.addInnerInterceptor(isi);
        }
        return interceptor;
    }

    /**
     * 分页拦截器之后的插件
     *
     * @return List<InnerInterceptor>
     */
    protected List<InnerInterceptor> getPaginationAfterInnerInterceptor() {
        return Collections.emptyList();
    }

    /**
     * 分页拦截器之前的插件
     *
     * @return List<InnerInterceptor>
     */
    protected List<InnerInterceptor> getPaginationBeforeInnerInterceptor() {
        return Collections.emptyList();
    }

    /**
     * Mybatis Plus 注入器
     * @return DatabaseProperties
     */
    @Bean("myBatisMetaObjectHandler")
    @ConditionalOnMissingBean
    @ConditionalOnExpression(MybatisExtraProperties.PLUS_META_OBJECT_HANDLER_SPEL)
    public MetaObjectHandler myBatisMetaObjectHandler() {
        return new MyBatisMetaObjectHandler(properties);
    }

    /**
     * 自定义方法增强注入
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public CustomSqlInjector getMySqlInjector() {
        return new CustomSqlInjector();
    }

}
